package com.pan.note.im.netty;

import com.alibaba.fastjson.JSON;
import com.pan.note.im.dto.GameDataDto;
import com.pan.note.im.dto.GameDataUploadDto;
import com.pan.note.im.dto.WebSocketMsgDto;
import com.pan.note.im.service.SnakeDataProcessService;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 自定义处理类
 */
@Component
@ChannelHandler.Sharable
@Slf4j
public class NettyWebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    /**
     * 定义通道列表
     */
    public static ConcurrentHashMap<String, Channel> channelMap = new ConcurrentHashMap<>();

    /**
     * 通道就绪事件
     *
     * @param ctx 上下文
     * @throws Exception 异常
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        log.info("有新的连接：{}",channel.id().asLongText());
    }


    /**
     * 通道未就绪--channel下线
     * @param ctx 上下文
     * @throws Exception 异常
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        /**
         * 根据channel得出用户id，然后将对应id的绑定删除，最后关闭通道
         */
        Channel channel = ctx.channel();
        Object key = getKey(channelMap, channel);
        SnakeDataProcessService.playerIsStart.put(key.toString(),false);
        channelMap.remove(key);
        log.info("删除channelMap中的通道");
        channel.close();
        log.info("通道关闭");
    }

    /**
     * 读就绪事件
     *
     * @param ctx 上下文
     * @param textWebSocketFrame 传输文本
     * @throws Exception 异常
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame textWebSocketFrame) throws Exception {
        String msg = textWebSocketFrame.text();
        /**
         * 读取出websocket数据后进行解析
         * 1.如果msgtype为0，则是身份认证数据，应该进行token解析，得到用户id和当前channel进行绑定
         * 2.如果msgtype为1，则为正常的聊天信息，则进行一对一或一对多转发
         */
        log.info("msg:{}",msg);
        // 解析消息
        Channel channel = ctx.channel();
        // 解析websocket数据，得到WebSocketMsgDto对象
        WebSocketMsgDto webSocketMsgDto = JSON.parseObject(msg, WebSocketMsgDto.class);
        switch (webSocketMsgDto.getMsgType()){
            case 0:
                // 将此用户和此频道绑定
                channelMap.put(webSocketMsgDto.getAccountId(),channel);
                log.info("用户和通道绑定");
                break;
            case 1:
                // 房间中两个人满，可以开始游戏
                String[] split = webSocketMsgDto.getMsgInfo().split(",");
                SnakeDataProcessService.startGame(split[0],split[1]);
                break;
            case 2:
                // 上传游戏数据，玩家id->蛇的移动方向
                SnakeDataProcessService.playerDir.put(webSocketMsgDto.getAccountId(),webSocketMsgDto.getMsgInfo());
                break;
            case 5:
                /**
                 * 处理心跳，原路返回一个pang
                 */
                 if(webSocketMsgDto.getMsgInfo().equals("ping")){
                     WebSocketMsgDto pang = new WebSocketMsgDto();
                     pang.setMsgType(5);
                     pang.setMsgInfo("pang");
                     String jsonString = JSON.toJSONString(pang);
                     channel.writeAndFlush(new TextWebSocketFrame(jsonString));
                     log.info("发送心跳");
                 }
                 break;
            default:
                break;
        }
    }

    public static void sendGameData(List<GameDataDto> dtoList,List<Integer> food){
        /**
         * 向双方玩家都要发送对方蛇的位置
         */
        dtoList.forEach(dto -> {
            Channel channel = channelMap.get(dto.getPlayerId());
            WebSocketMsgDto snakeData = new WebSocketMsgDto();
            WebSocketMsgDto foodData = new WebSocketMsgDto();
            snakeData.setMsgType(6);
            snakeData.setAccountId(dto.getPlayerId());
            snakeData.setMsgInfo(JSON.toJSONString(dtoList));
            String snakeJson = JSON.toJSONString(snakeData);
            channel.writeAndFlush(new TextWebSocketFrame(snakeJson));
            foodData.setMsgType(7);
            foodData.setAccountId(dto.getPlayerId());
            foodData.setMsgInfo(JSON.toJSONString(food));
            String foodJson = JSON.toJSONString(foodData);
            channel.writeAndFlush(new TextWebSocketFrame(foodJson));
            log.info("向前端发送游戏数据");
        });
    }

    /**
     * 异常处理
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
    }

    /**
     * 根据value查询本地channel的key
     * @return key
     */
    public static Object getKey(ConcurrentHashMap map, Object value){
        for(Object key: map.keySet()){
            if(map.get(key).equals(value)){
                return key;
            }
        }
        return null;
    }
}
